/*
 * Author : lihu
 * Email  : lilachue@163.com
 * QQ     : 11983222, send xini message to call me add u
 * All rights reserved!
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xini.h"

static XINI_NODE *__xnode_calloc( XINI *h, int n )
{
	void *p = calloc(n, sizeof( XINI_NODE ) ) ;
	if ( p )
		h->dbg_mem_alloc_cnt ++ ;
	return p ;
}

static void __xnode_free( XINI *h, XINI_NODE **p )
{
	if ( p && *p ) {
		free( *p ) ;
		h->dbg_mem_free_cnt ++ ;
		*p = NULL ;
	}
	return ;
}

static int __section_node_add( XINI *h, XINI_NODE *sn, int check_exist )
{
	XINI_NODE *t ;

	/* check if node b already exist */
	if ( check_exist ) {
		for ( t = h->section_head ; t ; t = t->next ) {
			if ( eq_str( t->data, sn->data ) )
				return XE_EXIST ;
		}
	}
	if ( !h->section_head )
		h->section_head = sn ;
	/* append it to section list tail */
	if ( h->section_tail )
		h->section_tail->next = sn ;
	sn->prev = h->section_tail ;
	h->section_tail = sn ;
	h->section_tail->next = NULL ;
	return XE_NONE ;
}

static int __section_node_del( XINI *h, XINI_NODE *sn )
{
	if ( sn->next == NULL )
	{
		if ( sn->prev == NULL )
		{
			h->section_head = h->section_tail = NULL ;
		}
		else
		{
			h->section_tail = h->section_tail->prev ;
			h->section_tail->next = NULL ;
		}
	}
	else
	{
		if ( sn->prev == NULL )
		{
			h->section_head = h->section_head->next ;
			h->section_head->prev = NULL ;
		}
		else
		{
			sn->prev->next = sn->next ;
			sn->next->prev = sn->prev ;
		}
	}
	sn->next = sn->prev = NULL ;
	return XE_NONE ;
}

static int __key_node_add( XINI *h, XINI_NODE *sn, XINI_NODE *kn, int check_exist )
{
	XINI_NODE *t ;

	/* check if node b already exist */
	if ( check_exist ) {
		for ( t = sn->u1.key_head ; t ; t = t->next ) {
			if ( eq_str( t->data, kn->data ) )
				return XE_EXIST ;
		}
	}

	if ( !sn->u1.key_head )
		sn->u1.key_head = kn ;
	/* append it to key list tail */
	if ( sn->u2.key_tail )
		sn->u2.key_tail->next = kn ;
	kn->prev = sn->u2.key_tail ;
	sn->u2.key_tail = kn ;
	sn->u2.key_tail->next = NULL ;
	return XE_NONE ;
}

static int __key_node_del( XINI *h, XINI_NODE *sn, XINI_NODE *kn )
{
	if ( kn->next == NULL )
	{
		if ( kn->prev == NULL )
		{
			sn->u1.key_head = sn->u2.key_tail = NULL ;
		}
		else
		{
			sn->u2.key_tail = sn->u2.key_tail->prev ;
			sn->u2.key_tail->next = NULL ;
		}
	}
	else
	{
		if ( kn->prev == NULL )
		{
			sn->u1.key_head = sn->u1.key_head->next ;
			sn->u1.key_head->prev = NULL ;
		}
		else
		{
			kn->prev->next = kn->next ;
			kn->next->prev = kn->prev ;
		}
	}
	kn->next = kn->prev = NULL ;
	return XE_NONE ;
}

static XINI_NODE *__section_node_find( XINI *h, char *s )
{
	XINI_NODE *sn = NULL ;

	if ( !h || !s )
		return NULL ;

	for ( sn = h->section_head ; sn ; sn = sn->next ) {
		if ( eq_str( sn->data, s ) )
			break ;
	}
	return sn ;
}

static XINI_NODE *__key_node_find( XINI *h, XINI_NODE *sn, char *k )
{
	XINI_NODE *kn = NULL ;

	if ( !h || !sn || !k )
		return NULL ;

	for ( kn = sn->u1.key_head ; kn ; kn = kn->next ) {
		if ( eq_str( kn->data, k ) )
			break ;
	}
	return kn ;
}

int xini_section_create( XINI *h, char *s )
{
	XINI_NODE *sn ;

	if ( !h || !s )
		return XE_MEM ;

	sn = __section_node_find( h, s ) ;
	if ( sn )
		return XE_EXIST ;

	sn = __xnode_calloc( h, 1 ) ;
	if ( !sn )
		return XE_MEM ;
	os_strncpy( sn->data, s, XINI_NODE_DATA_LEN ) ;
	__section_node_add( h, sn, 0 ) ;
	return XE_NONE ;
}

int xini_section_remove( XINI *h, char *s )
{
	XINI_NODE *sn, *kn, *t ;

	if ( !h || !s )
		return XE_MEM ;

	sn = __section_node_find( h, s ) ;
	if ( sn ) {
		for ( kn = sn->u1.key_head ; kn ; ) {
			if ( kn->u2.child )
				__xnode_free ( h, &(kn->u2.child) ) ;
			t = kn->next ;
			__xnode_free ( h, &kn ) ;
			kn = t ;
		}
		__section_node_del( h, sn ) ;
		__xnode_free( h, &sn ) ;
		return XE_NONE ;
	}
	return XE_NEXIST ;
}

int  xini_section_modify( XINI *h, char *s, char *d )
{
	XINI_NODE *sn ;

	if ( !h || !s )
		return XE_MEM ;

	sn = __section_node_find( h, s ) ;
	if ( sn ) {
		os_strncpy( sn->data, d, XINI_NODE_DATA_LEN ) ;
		return XE_NONE ;
	}
	return XE_NEXIST ;
}

int xini_key_create( XINI *h, char *s, char *k )
{
	XINI_NODE *sn, *kn ;

	if ( !h || !s || !k )
		return XE_MEM ;

	sn = __section_node_find( h, s ) ;
	/* section node not exist, create it automatically */
	if ( !sn ) {
		sn = __xnode_calloc( h, 1 ) ;
		if ( !sn )
			return XE_MEM ;
		os_strncpy( sn->data, s, XINI_NODE_DATA_LEN ) ;
		__section_node_add( h, sn, 0 ) ;
	}

	kn = __key_node_find( h, sn, k ) ;
	if ( kn )
		return XE_EXIST ;

	kn = __xnode_calloc( h, 1 ) ;
	if ( !kn )
		return XE_MEM ;
	os_strncpy( kn->data, k, XINI_NODE_DATA_LEN ) ;
	__key_node_add( h, sn, kn, 0 ) ;
	return XE_NONE ;
}

int xini_key_remove( XINI *h, char *s, char *k )
{
	XINI_NODE *sn, *kn ;

	if ( !h || !s || !k )
		return XE_MEM ;

	sn = __section_node_find( h, s ) ;
	if ( sn ) {
		kn = __key_node_find( h, sn, k ) ;
		if ( kn ) {
			if ( kn->u2.child )
				__xnode_free( h, &(kn->u2.child ) ) ;
			__key_node_del( h, sn, kn ) ;
			__xnode_free( h, &kn ) ;
			return XE_NONE ;
		}
	}
	return XE_NEXIST ;
}

int xini_key_modify( XINI *h, char *s, char *k, char *d )
{
	XINI_NODE *sn, *kn ;

	if ( !h || !s || !k || !d )
		return XE_MEM ;

	sn = __section_node_find( h, s ) ;
	if ( sn ) {
		kn = __key_node_find( h, sn, k ) ;
		if ( kn ) {
			os_strncpy( kn->data, d, XINI_NODE_DATA_LEN ) ;
			return XE_NONE ;
		}
	}
	return XE_NEXIST ;
}

/******************************************************************************/

int xini_data_create( XINI *h, char *s, char *k, char *d )
{
	XINI_NODE *sn = NULL, *kn = NULL, *dn = NULL ;

	if ( !h || !s || !k || !d )
		return XE_MEM ;

	sn = __section_node_find( h, s ) ;
	if ( !sn ) {
		sn = __xnode_calloc( h, 1 ) ;
		if ( !sn )
			return XE_MEM ;
		os_strncpy( sn->data, s, XINI_NODE_DATA_LEN ) ;
		__section_node_add( h, sn, 0 ) ;
	}

	kn = __key_node_find( h, sn, k ) ;
	if ( !kn ) {
		kn = __xnode_calloc( h, 1 ) ;
		if ( !kn )
			return XE_MEM ;
		os_strncpy( kn->data, k, XINI_NODE_DATA_LEN ) ;
		__key_node_add( h, sn, kn, 0 ) ;
	}

	dn = kn->u2.child ;
	if ( !dn )
		dn = __xnode_calloc( h, 1 ) ;
	if ( !dn )
		return XE_MEM ;
	os_strncpy( dn->data, d, XINI_NODE_DATA_LEN ) ;
	kn->u2.child = dn;
	dn->u1.parent = kn ;

	return XE_NONE ;
}

int xini_data_remove( XINI *h, char *s, char *k, char *d )
{
	XINI_NODE *sn, *kn ;

	if ( !h || !s || !k )
		return XE_MEM ;

	sn = __section_node_find( h, s ) ;	
	if ( !sn )
		return XE_NEXIST ;

	kn = __key_node_find( h, sn, k ) ;
	if ( !kn || !kn->u2.child )
		return XE_NEXIST ;

	if ( d && !eq_str( kn->u2.child->data, d ) ) 
		return XE_NEXIST ;

	__xnode_free( h, &(kn->u2.child) ) ;
	kn->u2.child = NULL ;

	return XE_NONE ;
}

int xini_data_modify( XINI *h, char *s, char *k, char *d, char *nd )
{
	XINI_NODE *sn, *kn ;

	if ( !h || !s || !k )
		return XE_MEM ;

	sn = __section_node_find( h, s ) ;	
	if ( !sn )
		return XE_NEXIST ;

	kn = __key_node_find( h, sn, k ) ;
	if ( !kn || !kn->u2.child )
		return XE_NEXIST ;

	if ( d && !eq_str( kn->u2.child->data, d ) )
		return XE_NEXIST ;

	os_strncpy(kn->u2.child->data, nd, XINI_NODE_DATA_LEN ) ;

	return XE_NONE ;
}

int xini_section_cursor_eof( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	if ( !h->section_cursor )
		return XE_NSCURSOR ;

	if ( h->section_cursor->next )
		return XE_ERROR ;	

	return XE_NONE ;
}

int xini_section_cursor_set( XINI *h, char *s )
{
	if ( !h || !s )
		return XE_MEM ;

	if ( h->section_cursor && eq_str( h->section_cursor->data, s ) )
		return XE_NONE ;

	for ( h->section_cursor = h->section_head ; h->section_cursor ; h->section_cursor = h->section_cursor->next ) {
		if ( eq_str( h->section_cursor->data, s ) ) {
			h->key_cursor = h->section_cursor->u1.key_head ;
			return XE_NONE ;
		}
	}

	return XE_NEXIST ;
}

int xini_section_cursor_move_first( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	h->section_cursor = h->section_head ;
	if ( h->section_cursor )
		h->key_cursor = h->section_cursor->u1.key_head ;
	else
		h->key_cursor = NULL ;
	return XE_NONE ;
}

int xini_section_cursor_move_next( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	if ( h->section_cursor && h->section_cursor->next ) {
		h->section_cursor = h->section_cursor->next ;
		if ( h->section_cursor )
			h->key_cursor = h->section_cursor->u1.key_head ;
		else
			h->key_cursor = NULL ;
		return XE_NONE ;
	}
	return XE_ERROR ;
}

int xini_section_cursor_move_prev( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	if ( h->section_cursor && h->section_cursor->prev ) {
		h->section_cursor = h->section_cursor->prev ;
		if ( h->section_cursor )
			h->key_cursor = h->section_cursor->u1.key_head ;
		else
			h->key_cursor = NULL ;
		return XE_NONE ;
	}
	return XE_ERROR ;
}

int xini_section_cursor_move_last( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	h->section_cursor = h->section_tail ;
	if ( h->section_cursor )
		h->key_cursor = h->section_cursor->u1.key_head ;
	else
		h->key_cursor = NULL ;
	return XE_NONE ;
}

char* xini_section_cursor_data( XINI *h )
{
	if ( !h )
		return NULL ;

	if ( !h->section_cursor )
		return NULL ;
	return h->section_cursor->data ;
}

int xini_key_cursor_eof( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	if ( !h->section_cursor )
		return XE_NSCURSOR ;

	if ( !h->key_cursor )
		return XE_NKCURSOR ;

	if ( h->key_cursor->next )
		return XE_ERROR ;	

	return XE_NONE ;
}

int xini_key_cursor_set( XINI *h, char *k )
{
	if ( !h || !k )
		return XE_MEM ;

	if ( !h->section_cursor )
		return XE_NSCURSOR ;

	for ( h->key_cursor = h->section_cursor->u1.key_head ; h->key_cursor ; h->key_cursor = h->key_cursor->next ) {
		if ( eq_str( h->key_cursor->data, k ) )
			return XE_NONE ;
	}
	return XE_NEXIST ;
}

int xini_key_cursor_move_first( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	if ( !h->section_cursor )
		return XE_NSCURSOR ;

	h->key_cursor = h->section_cursor->u1.key_head ;
	return XE_NONE ;
}

int xini_key_cursor_move_next( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	if ( !h->section_cursor )
		return XE_NSCURSOR ;

	if ( !h->key_cursor || !h->key_cursor->next )
		return XE_ERROR ;

	h->key_cursor = h->key_cursor->next ;
	return XE_NONE ;
}

int xini_key_cursor_move_prev( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	if ( !h->section_cursor )
		return XE_NSCURSOR ;

	if ( !h->key_cursor || !h->key_cursor->prev)
		return XE_ERROR ;

	h->key_cursor = h->key_cursor->prev ;
	return XE_NONE ;
}

int xini_key_cursor_move_last( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	if ( !h->section_cursor )
		return XE_NSCURSOR ;

	h->key_cursor = h->section_cursor->u2.key_tail ;
	return XE_NONE ;
}

char * xini_key_cursor_data( XINI *h )
{
	if ( !h || !h->key_cursor )
		return NULL ;

	return h->key_cursor->data ;
}

char *xini_ptr_data_get( XINI *h, char *s, char *k )
{
	XINI_NODE *sn = NULL, *kn = NULL;

	if ( !h || !s || !k )
		return NULL ;

	if ( h->section_cursor && eq_str( h->section_cursor->data, s ) ) {
		sn = h->section_cursor ;
	}
	else {
		for ( sn = h->section_head ; sn ; sn = sn->next ) {
			if ( eq_str( sn->data, s ) )
				break ;
		}
	}

	/* not found section */
	if ( !sn )
		return NULL ;

	if ( h->key_cursor && h->key_cursor->u1.parent == sn && eq_str( h->key_cursor->data, k ) ) {
		kn = h->key_cursor ;		
	}
	else {
		for ( kn = sn->u1.key_head ; kn ; kn = kn->next ) {
			if ( eq_str( kn->data, k ) )
				break ;
		}
	}

	/* not found key */
	if ( !kn )
		return NULL ;

	if ( kn->u2.child == NULL )
		return NULL ;

	return kn->u2.child->data ;
}

int xini_dispose( XINI *h )
{
	XINI_NODE *k, *v, *k1, *v1 ;

	if ( !h )
		return XE_MEM ;

	for ( k = h->section_head ; k ; ) {
		for ( v = k->u1.key_head ; v ; ) {
			v1 = v->next ;
			if ( v->u2.child )
				__xnode_free( h, &(v->u2.child) ) ;
			__key_node_del( h, k, v ) ;
			__xnode_free( h, &v ) ;
			v = v1 ;
		}
		k1 = k->next ;
		__section_node_del( h, k ) ;
		__xnode_free( h, &k ) ;
		k = k1 ;
	}
	return XE_NONE ;
}

int xini_load( XINI *h, char *f )
{
#define XS_REC_NONE		1
#define XS_REC_SECT		2
#define XS_REC_KEY		3
#define XS_REC_DATA		4
#define XS_REC_REMARK	5

	int xs = XS_REC_NONE, dpos = 0, ret = XE_NONE ;
	unsigned char c ;
	XINI_NODE *n = NULL;
	FILE *fp = NULL ;

	if ( !h || !f )
		return XE_FILE ;

	xini_dispose( h ) ;
	
	fp = fopen( f, "r" ) ;
	if ( fp == NULL )
		return XE_FILE ;

	h->file = f ;

	while ( !feof( fp ) )
	{
		c = fgetc( fp ) ;
		switch( c )
		{
		case ';':
				if ( xs == XS_REC_NONE )
				{
				/*
				 * do nothing, but future will support remark
				 */
					xs = XS_REC_REMARK ;
				}
				else if ( xs == XS_REC_SECT )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_KEY )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_DATA )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_REMARK )
				{
				/*
				 * do nothing, but future will support remark
				 */
				}
				break ;
			case 0xff:
				if ( xs == XS_REC_NONE )
				{
					/* do nothing, just end of file */
				}
				else if ( xs == XS_REC_SECT )
				{
					__xnode_free( h, &n ) ;
					ret = XE_FMT ;
					goto err ;
				}
				else if ( xs == XS_REC_KEY )
				{
					__xnode_free( h, &n ) ;
					ret = XE_FMT ;
					goto err ;
				}
				else if ( xs == XS_REC_DATA )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos] = 0 ;
					/* add data node to key */
					h->key_cursor->u2.child = n ;
					n->u1.parent = h->key_cursor ;
					n->u2.child = NULL ;
					h->key_cursor->u1.parent = NULL ;
					xs = XS_REC_NONE ;
				}
				else if ( xs == XS_REC_REMARK )
				{
					/* do nothing now, just end of file */
				}
				break ;
			case '[' :
				if ( xs == XS_REC_NONE )
				{
					if ( ( n = __xnode_calloc( h, 1 ) ) == NULL ) {
						ret = XE_MEM ;
						goto err ;
					}
					dpos = 0 ;
					n->ntype = XINI_NTYPE_SECTION ;
					xs = XS_REC_SECT ;
				}
				else if ( xs == XS_REC_SECT )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_KEY )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_DATA )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_REMARK )
				{
					/* do nothing now */
				}
				break ;
			case ']' :
				if ( xs == XS_REC_NONE )
				{
					if ( ( n = __xnode_calloc( h, 1 ) ) == NULL ) {
						ret = XE_MEM ;
						goto err ;
					}
					dpos = 0 ;
					n->data[dpos++] = c ;
					n->ntype = XINI_NTYPE_KEY ;
					xs = XS_REC_KEY ;
				}
				else if ( xs == XS_REC_SECT )
				{
					if ( XE_EXIST == __section_node_add( h, n,  1 ) ) {
						xini_section_cursor_set( h, n->data ) ;
						__xnode_free( h, &n ) ;
					}
					else {
						h->section_cursor = n ;
						dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
						n->data[dpos] = 0 ;
					}
					xs = XS_REC_NONE ;
				}
				else if ( xs == XS_REC_KEY )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_DATA )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_REMARK )
				{
					/* do nothing now */
				}
				break ;
			case '=' :
				if ( xs == XS_REC_NONE )
				{
					/* when a new line start with '=' */
					ret = XE_FMT ;
					goto err ;
				}
				else if ( xs == XS_REC_SECT )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_KEY )
				{
					n->data[dpos] = 0 ;

					if ( !h->section_cursor ) {
						__xnode_free( h, &n ) ;
						ret = XE_FMT ;
						goto err ;
					}
					
					if ( XE_EXIST == __key_node_add( h, h->section_cursor, n, 1 ) ) {
						xini_key_cursor_set( h,  n->data ) ;
						__xnode_free( h, &n ) ;
					}
					else {
						h->key_cursor = n ;
					}

					xs = XS_REC_DATA ;

					if ( ( n = __xnode_calloc( h, 1 ) ) == NULL ) {
						ret = XE_MEM ;
						goto err ;
					}
					dpos = 0 ;
					n->ntype =  XINI_NTYPE_DATA ;
				}
				else if ( xs == XS_REC_DATA )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_REMARK )
				{
					/* do nothing now */
				}
				break ;
			case '\r':
			case '\n':
				if ( xs == XS_REC_NONE )
				{
					/* do nothing now */
				}
				else if ( xs == XS_REC_SECT )
				{
					__xnode_free( h, &n ) ;
					ret = XE_FMT ;
					goto err ;
				}
				else if ( xs == XS_REC_KEY )
				{
					__xnode_free( h, &n ) ;
					ret = XE_FMT ;
					goto err ;
				}
				else if ( xs == XS_REC_DATA )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos] = 0 ;

					if ( !h->key_cursor ) {
						__xnode_free( h, &n ) ;
						ret = XE_FMT ;
						goto err ;
					}

					if ( os_strlen( n->data ) > 0 ) {
						h->key_cursor->u2.child = n ;
						n->u1.parent = h->key_cursor ;
						n->u2.child = NULL ;
						h->key_cursor->u1.parent = NULL ;
					}
					else {
						__xnode_free( h, &n ) ;
					}
					xs = XS_REC_NONE ;					
				}
				else if ( xs == XS_REC_REMARK )
				{
					xs = XS_REC_NONE ;
				}
				break ;
			default:
				if ( xs == XS_REC_NONE )
				{
					if ( ( n = __xnode_calloc( h, 1 ) ) == NULL ) {
						ret = XE_MEM ;
						goto err ;
					}
					dpos = 0 ;
					n->data[dpos++] = c ;
					n->ntype = XINI_NTYPE_KEY ;
					xs = XS_REC_KEY ;
				}
				else if ( xs == XS_REC_SECT )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_KEY )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_DATA )
				{
					dpos = dpos < XINI_NODE_DATA_LEN -1 ? dpos : XINI_NODE_DATA_LEN - 1 ; 
					n->data[dpos++] = c ;
				}
				else if ( xs == XS_REC_REMARK )
				{
					/* do nothing now */
				}
				break ;
		}
	}
	h->section_cursor = h->section_head ;
	if ( h->section_cursor )
		h->key_cursor = h->section_cursor->u1.key_head ;
	else
		h->key_cursor = NULL ;
err:
	fclose( fp ) ;
	return ret ;
}

int xini_reload( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	return xini_load( h, h->file ) ;
}

int xini_save_as( XINI *h, char *file )
{
	FILE *fp ;
	XINI_NODE *k, *v ;

	if ( !h )
		return XE_MEM ;
	if ( !file )
		return XE_FILE ;

	if ( ( fp = fopen( file, "w" ) ) == NULL )
		return XE_FILE ;

	for ( k = h->section_head ; k ; k = k->next ) {
		fprintf( fp, "[%s]\r\n", k->data ) ;
		for ( v = k->u1.key_head ; v ; v = v->next ) {
			if ( v->u2.child )
				fprintf( fp, "%s=%s\r\n", v->data, v->u2.child->data ) ;
			else
				fprintf( fp, "%s=\r\n", v->data ) ;
		}
		fprintf( fp, "\r\n" ) ;
	}

	fclose( fp ) ;

	return XE_NONE ;
}

int xini_save( XINI *h )
{
	return xini_save_as( h, h->file ) ;
}

int xini_initial( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	os_memset( h, 0, sizeof( XINI ) ) ;

	h->load = xini_load ;
	h->reload = xini_reload ;
	h->dispose = xini_dispose ;
	h->save = xini_save ;
	h->save_as = xini_save_as ;

	h->section_create = xini_section_create ;
	h->section_remove = xini_section_remove ;
	h->section_modify = xini_section_modify ;

	h->key_create = xini_key_create ;
	h->key_remove = xini_key_remove ;
	h->key_modify = xini_key_modify ;

	h->data_create = xini_data_create ;
	h->data_remove = xini_data_remove ;
	h->data_modify = xini_data_modify ;

	h->section_cursor_eof = xini_section_cursor_eof ;
	h->section_cursor_set = xini_section_cursor_set ;
	h->section_cursor_move_first = xini_section_cursor_move_first ;
	h->section_cursor_move_last = xini_section_cursor_move_last ;
	h->section_cursor_move_next = xini_section_cursor_move_next ;
	h->section_cursor_move_prev = xini_section_cursor_move_prev ;
	h->section_cursor_data = xini_section_cursor_data ;

	h->key_cursor_eof = xini_key_cursor_eof ;
	h->key_cursor_set = xini_key_cursor_set ;
	h->key_cursor_move_first = xini_key_cursor_move_first ;
	h->key_cursor_move_last = xini_key_cursor_move_last ;
	h->key_cursor_move_next = xini_key_cursor_move_next ;
	h->key_cursor_move_prev = xini_key_cursor_move_prev ;
	h->key_cursor_data = xini_key_cursor_data ;

	h->ptr_data_get = xini_ptr_data_get ;
	return XE_NONE ;
}

int xini_destroy( XINI *h )
{
	if ( !h )
		return XE_MEM ;

	xini_dispose( h ) ;
	os_memset( h, 0, sizeof( XINI ) ) ;
	return XE_NONE ;
}

